package com.ld.blecardlibrarydes.ble;

import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

import com.ld.blecardlibrarydes.utils.DataUtil;
import com.ld.blecardlibrarydes.utils.DesUtil;
import com.ld.blecardlibrarydes.utils.TripleDes;

import java.util.UUID;

/**
 * Service for managing connection and data communication with a GATT server hosted on a
 * given Bluetooth LE device.
 */
public class BluetoothLeService extends Service {
    private final static String TAG = BluetoothLeService.class.getSimpleName();

    private BluetoothManager mBluetoothManager;
    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothGattService myGattService;
    private BluetoothGattCharacteristic read_write,notification_characteristic;
    private String mBluetoothDeviceAddress;
    private BluetoothGatt mBluetoothGatt;
    private int mConnectionState = STATE_DISCONNECTED;

    private static final int STATE_DISCONNECTED = 0;
    private static final int STATE_CONNECTING = 1;
    private static final int STATE_CONNECTED = 2;

    private static final UUID service_uuid = UUID
            .fromString(SampleGattAttributes.SERVICE);
    private static final UUID characteristic1_uuid = UUID
            .fromString(SampleGattAttributes.CHARACTERISTIC_1);
    private static final UUID notification1_uuid = UUID.fromString(SampleGattAttributes.NOTIFICATION_1);

    public static boolean Running = false;

    private NfcbleModel nfcbleModel;

    private int DataCount = 0;
    private int count = 0;

    private StringBuffer stringBuffer;

    private DataChangeListener dataChangeListener;

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
//            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                dataChangeListener.Connected();
                mConnectionState = STATE_CONNECTED;
                mBluetoothGatt.getServices();
                mBluetoothGatt.discoverServices();
                Log.i("aaa", "onConnectionStateChange.");
                Running = true;
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                dataChangeListener.Disconnected();
                mConnectionState = STATE_DISCONNECTED;
                Log.d(TAG, "Disconnected from GATT server.");
                mBluetoothGatt.close();
                mBluetoothDeviceAddress = null;
            }
        }


        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            Log.i("aaa","onServicesDiscovered");
            if (status == BluetoothGatt.GATT_SUCCESS) {
                if (mBluetoothGatt != null) {
                    myGattService = mBluetoothGatt.getService(service_uuid);
                }
                if (myGattService != null) {
                    String aaa = myGattService.getUuid().toString();
                    read_write = myGattService
                            .getCharacteristic(characteristic1_uuid);
                    notification_characteristic = myGattService.getCharacteristic(notification1_uuid);

                }
                if (notification_characteristic != null) {
                    Log.i("aaa","notification_characteristic success");
                    setCharacteristicNotification(notification_characteristic, true);
                }
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
//            if (status == BluetoothGatt.GATT_SUCCESS) {
//                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
//            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            final byte[] data = characteristic.getValue();
            if (data != null && data.length > 0) {
//                Log.i("aaa","data:"+DataUtil.getStringByBytes(data));
                if (DataCount == 0) {
                    byte[] newData = DataUtil.getBytesByString(TripleDes.DES_3(DataUtil.getStringByBytes(data).substring(0,16), DesUtil.masterKey,1));
                    if (newData[0] == (byte)0xB0) {
                        if (newData[3] == 0x00) {
                            if (data.length < 20) {
                                dataChangeListener.NormalData(newData, DataCount);
                            } else {
                                int hight = newData[1] & 0xff;
                                int low = newData[2] & 0xff;
                                DataCount = hight * 256 + low;
                                count = DataCount + 8 - DataCount % 8;
                                stringBuffer.delete(0, stringBuffer.length());
                                stringBuffer.append(DataUtil.getStringByBytes(data));
                            }
                        } else {
                            dataChangeListener.ErrorData(newData[3]);
                        }
                    }
                }else {
                    stringBuffer.append(DataUtil.getStringByBytes(data));
                    if (count - 16 > 20){
                        count = count - 20;
                    }else {
                        StringBuffer buffer = new StringBuffer();
                        String mydata = stringBuffer.toString();
//                        Log.i("aaa","mydata:"+mydata);
                        for (int i = 0;i<(mydata.length()/16);i++){
                            buffer.append(TripleDes.DES_3(mydata.substring(16*i,16*i+16), DesUtil.masterKey,1));
                        }

                        dataChangeListener.NormalData(DataUtil.getBytesByString(buffer.toString()),DataCount);
                        DataCount = 0;
                        stringBuffer.delete(0, stringBuffer.length());
                    }
                }
            }else {
                return;
            }
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
        }
    };

    public void broadcastUpdate(final String action) {
        final Intent intent = new Intent(action);
        sendBroadcast(intent);
    }

    public void setDataChangeListener(DataChangeListener dataChangeListener){
        this.dataChangeListener = dataChangeListener;
    }

    public class LocalBinder extends Binder {
        public BluetoothLeService getService() {
            return BluetoothLeService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        // After using a given device, you should make sure that BluetoothGatt.close() is called
        // such that resources are cleaned up properly.  In this particular example, close() is
        // invoked when the UI is disconnected from the Service.
        return super.onUnbind(intent);
    }

    private final IBinder mBinder = new LocalBinder();

    /**
     * Initializes a reference to the local Bluetooth adapter.
     *
     * @return Return true if the initialization is successful.
     */
    public boolean initialize() {
        // For API level 18 and above, get a reference to BluetoothAdapter through
        // BluetoothManager.
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            if (mBluetoothManager == null) {
                Log.e(TAG, "Unable to initialize BluetoothManager.");
                return false;
            }
        }
        mBluetoothAdapter = mBluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
            Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
            return false;
        }
        stringBuffer = new StringBuffer();
        nfcbleModel = new NfcbleModel();
        return true;
    }

    public void writeStringToGatt(byte[] data) {
//        Log.i("aaa",DataUtil.getStringByBytes(data));
        DataCount = 0;
        if (read_write == null || mBluetoothGatt == null) {
            Running = false;
        } else if (data != null) {
            read_write.setValue(data);
            mBluetoothGatt.writeCharacteristic(read_write);
//            Log.i("aaa", "write_to_gatt");
        }
    }

    public void writeData(int function,byte[] data){
        nfcbleModel.writeBytes(function, data, this);
    }

    public void writeData(byte[] data){
        nfcbleModel.writeBytes(data,this);
        dataChangeListener.SendData(data);
    }

    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {
            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }

        // Previously connected device.  Try to reconnect.
        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
                && mBluetoothGatt != null) {
            Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
            if (mBluetoothGatt.connect()) {
                mConnectionState = STATE_CONNECTING;
                return true;
            } else {
                return false;
            }
        }

        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            Log.w(TAG, "Device not found.  Unable to connect.");
            return false;
        }
        // We want to directly connect to the device, so we are setting the autoConnect
        // parameter to false.
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        Log.d(TAG, "Trying to create a new connection.");
        mBluetoothDeviceAddress = address;
        mConnectionState = STATE_CONNECTING;
        return true;
    }

    public void disconnect() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.disconnect();
        mBluetoothDeviceAddress = null;
    }

    public void close() {
        if (mBluetoothGatt == null) {
            return;
        }
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    }

    public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.readCharacteristic(characteristic);
    }

    public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
                                              boolean enabled) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
        if (characteristic1_uuid.equals(characteristic.getUuid())) {
            BluetoothGattDescriptor descriptor = characteristic
                    .getDescriptor(UUID
                            .fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
            descriptor
                    .setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            mBluetoothGatt.writeDescriptor(descriptor);
        }else if (notification1_uuid.equals(characteristic.getUuid())) {
            BluetoothGattDescriptor descriptor = characteristic
                    .getDescriptor(UUID
                            .fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
            descriptor
                    .setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            mBluetoothGatt.writeDescriptor(descriptor);
        }
    }
    public void getRssi() {
        if(mBluetoothGatt != null) {
            mBluetoothGatt.readRemoteRssi();
        }
    }

    public interface DataChangeListener{
        void Disconnected();
        void Connected();
        void NormalData(byte[] data, int length);
        void ErrorData(byte errorData);
        void SendData(byte[] sendData);
    }
}
